const path = require('path');
const { traversaDir } = require('./util');

/**
 * 遍历并载入文件夹下所有的js文件
 * @param {string} dir 文件夹路径
 * @param {*} callback 每找到一个js文件，都会将require(xxx.js)传入callback
 * @returns 按目录层级映射好的一个obj
 */
async function traversaDirJs(dir, callback) {
  const obj = {};
  const fileList = await traversaDir(dir);
  for (const file of fileList) {
    if (path.extname(file) !== '.js') {
      continue;
    }
    const relativePath = file.replace(path.resolve(dir), '');
    const layerList = relativePath.split(path.sep).filter((item) => {
      return item !== '';
    }).slice(0, -1);
    let layerObj = obj;
    for (const layer of layerList) {
      if (!layerObj[layer]) {
        layerObj[layer] = {};
      }
      layerObj = layerObj[layer];
    }
    const instance = require(file);
    const instaceName = path.basename(file, '.js');
    layerObj[instaceName] = callback(instance);
  }
  return obj;
}

/**
 * 将类方法转换为controller
 * @param {Class} controllerClass controller类
 * @param {function} key 方法名
 */
function toController(controllerClass, key, service) {
  return function Controller(ctx) {
    // 每个请求都会实例化一个controller，这样做是为了隔离不同的请求，在一个请求链上都使用一个this
    const controller = new controllerClass(ctx, service, controllerClass.name);
    return controller[key].call(controller);
  };
}

/**
 * 解析controller类中的可执行函数
 * @param {import('./contorller').Controller} controllerClass 
 * @param {import('../index').Service} service 
 */
function parseController(controllerClass, service) {
  const ret = {};
  let proto = controllerClass.prototype;
  while (proto !== Object.prototype) {
    // 如果想在原型链上过滤非必要方法，可以将方法设置为不可遍历
    const keys = Object.getOwnPropertyNames(proto);
    for (const key of keys) {
      if (key === 'constructor' || typeof proto[key] !== 'function') {
        continue;
      }
      // 遍历原型链时是逐步溯源向上的，子类重写的方法应该避免被父类覆盖
      if (Object.hasOwnProperty.call(ret, key)) {
        continue;
      }
      ret[key] = toController(controllerClass, key, service);
    }
    proto = Object.getPrototypeOf(proto);
  }
  return ret;
}

/**
 * 载入service层
 * @param {string} servicePath 
 */
exports.loadService = async function (servicePath) {
  return traversaDirJs(servicePath, (serviceClass) => serviceClass);
};

/**
 * 载入controller层
 * @param {string} controllerPath controller文件夹
 * @param {} service service层
 */
exports.loadController = async function(controllerPath, service) {
  return traversaDirJs(controllerPath, (controllerClass) => {
    return parseController(controllerClass, service);
  });
};

/**
 * 载入socket/controller层
 * @param {string} loadSocketControllerPath socket/controller文件夹
 * @param {string} service service层
 */
exports.loadSocketController = async function (loadSocketControllerPath, service) {
  return traversaDirJs(loadSocketControllerPath, (controllerClass) => {
    return parseController(controllerClass, service);
  });
};

/**
 * 载入router层
 * @param {string} routerPath router文件夹
 * @param {Application} app 
 */
exports.loadRouter = async function(routerPath, app) {
  return traversaDirJs(routerPath, (routerFunc) => {
    return routerFunc(app);
  });
};
